package me.zhengjie.minbearwcs.serivce.mission.generate.outbound;


import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.minbearwcs.dao.mission.inf.WcsMissionDao;
import me.zhengjie.minbearwcs.dao.outbound.inf.*;
import me.zhengjie.minbearwcs.dao.storage.inf.MediumStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.SmallStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.StorageDao;
import me.zhengjie.minbearwcs.entity.DTO.outbound.wcs.MediumStockUp;
import me.zhengjie.minbearwcs.entity.DTO.transfer.Tuple;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.WcsMission;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.status.OutboundSortMissionStatus;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.status.OutboundWholePalletMissionStatus;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.type.MissionType;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.OutboundOrder;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.OutboundOrderDetail;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.common.OutboundContentPool;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.record.OutboundRestAreaSearchPo;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.record.OutboundTaskRecord;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.record.StoragePalletSearchPo;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.stockUp.MediumStorageAndFloorNum;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.stockUp.OutboundStockUpRecord;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.type.StorageType;
import me.zhengjie.minbearwcs.serivce.mission.generate.MissionGenerator;
import me.zhengjie.minbearwcs.serivce.mission.generate.type.MissionGenType;
import me.zhengjie.minbearwcs.serivce.mission.storageProcessor.locker.Floor5Or6.OutboundStorage5Or6FloorLocker;
import me.zhengjie.minbearwcs.utils.IdWorker;
import me.zhengjie.observer.caster.WcsMulticaster;
import me.zhengjie.observer.event.DefaultEvent;
import me.zhengjie.observer.type.inbound.InboundEventType;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Component
@RequiredArgsConstructor
public class OutboundMissionGenerator implements MissionGenerator<String> {

    private final OutboundOrderDao outboundOrderDao;
    private final OutboundOrderDetailDao outboundOrderDetailDao;
    private final WcsMulticaster wcsMulticaster;
    private final MediumStorageDao mediumStorageDao;
    private final StorageDao storageDao;
    private final SmallStorageDao smallStorageDao;
    private final OutboundDao outboundDao;
    private final IdWorker idWorker;
    private final OutboundStockUpRecordDao outboundStockUpRecordDao;
    private final WcsMissionDao wcsMissionDao;
    private final OutboundTaskRecordDao outboundTaskRecordDao;
    private final OutboundStorage5Or6FloorLocker outboundStorage5Or6FloorLocker;


    @Override
    public MissionGenType type() {
        return MissionGenType.OUTBOUND;
    }

    @Override
//    @Transactional
    public Tuple<String> generate(Object source) {
        /**
         *
         * 保证库存足够才下单》》》》一张单只有一种物料
         *
         * 1.拿到所有单，按优先级排序
         * 2。拿到detail，
         *  2.1 扣待分拣区，生成wcs任务
         *  2.2 不够，扣库区，生成wcs任务
         *  2.3 还有，找板分拣，生成wcs任务
         *
         *
         */

        List<String> outboundOrderCodeList = (List<String>) source;

        //1.拿到所有单，按优先级排序
        List<OutboundOrder> outboundOrderList = outboundOrderDao.selectInOutboundCodeOrder(outboundOrderCodeList);

        //2。拿到detail，
        for (OutboundOrder outboundOrder : outboundOrderList) {
            // 更新工单状态
            wcsMulticaster.multicastEvent(DefaultEvent.create(outboundOrder.getOutboundOrderCode()), InboundEventType.OUTBOUND_ORDER_START);
            // 生成wcs任务
            handleOutboundOrder(outboundOrder);

        }


        return Tuple.tuple(null, true);
    }


    private void handleOutboundOrder(OutboundOrder outboundOrder) {
        List<OutboundOrderDetail> outboundOrderDetailList = outboundOrderDetailDao.selectByOutboundOrderCode(outboundOrder.getOutboundOrderCode());
        //2.1 扣待分拣区，生成wcs任务
        findWaitSortArea(outboundOrder, outboundOrderDetailList);


        for (OutboundOrderDetail outboundOrderDetail : outboundOrderDetailList) {
            //2.2 不够，扣库区，生成wcs任务
            findStorageArea(outboundOrderDetail, outboundOrder);
            // 2.3 找尾数区整板，生成wcs任务
            findMantissaArea(outboundOrderDetail, outboundOrder);
            // 2.4 还有，找板分拣，生成wcs任务
            findRestPallet(outboundOrderDetail, outboundOrder);
            outboundOrderDetailDao.updateCompleteNumById(outboundOrderDetail.getId(), outboundOrderDetail.getCompleteNum());
        }


    }


    private void findWaitSortArea(OutboundOrder outboundOrder, List<OutboundOrderDetail> outboundOrderDetailList) {
        // 找全部待分拣区中储位
        List<String> waitOutboundMediumStorageList = storageDao.selectCanOutboundMediumCode(
                StorageType.WAIT_SORT_AREA_5F,
                StorageType.WAIT_SORT_AREA_6F,
                outboundOrder.getOutboundOrderCode()
        );

        for (String waitMediumStorage : waitOutboundMediumStorageList) {
            List<StoragePalletSearchPo> palletMediumStorageSearchPoList = outboundDao.selectStoragePalletByMediumStorage(waitMediumStorage);
            for (StoragePalletSearchPo searchPo : palletMediumStorageSearchPoList) {
                if (searchPo.getPalletCode() == null) continue;

                // 减数量
                List<OutboundOrderDetail> collect = outboundOrderDetailList.stream()
                        .filter(a -> a.getMaterialCode().equals(searchPo.getMaterialCode()))
                        .collect(Collectors.toList());

                if (collect.isEmpty()) continue;


                int needNum = collect.stream()
                        .mapToInt(a -> a.getRequireNum() - a.getCompleteNum())
                        .sum();

                if (needNum >= searchPo.getMaterialNum()) {
                    // 需求数大于这栈板数，就出
                    // 1.先减数
                    int materialNum = searchPo.getMaterialNum();
                    for (OutboundOrderDetail orderDetail : collect) {
                        if (materialNum == 0) break;
                        int lackNum = orderDetail.getRequireNum() - orderDetail.getCompleteNum();
                        if (lackNum >= materialNum) {
                            // 能覆盖完
                            orderDetail.setCompleteNum(orderDetail.getCompleteNum() + materialNum);
                            materialNum = 0;
                        } else {
                            // 覆盖不完
                            orderDetail.setCompleteNum(orderDetail.getRequireNum());
                            materialNum = materialNum - lackNum;
                        }
                    }


                    // 2.生成wcs任务
                    OutboundOrderDetail outboundOrderDetail = collect.get(0);
                    String startStorage = outboundStockUpRecordDao.selectStartSmallStorageByOutboundOrderAndPallet(outboundOrderDetail.getOutboundOrderCode(), searchPo.getPalletCode());

                    OutboundTaskRecord outboundTaskRecord = new OutboundTaskRecord(null, "outbound_record_" + idWorker.nextId(), outboundOrderDetail.getOutboundOrderCode(),
                            outboundOrderDetail.getOutboundDetailId(), searchPo.getPalletCode(), searchPo.getMaterialCode(),
                            searchPo.getMaterialNum(), searchPo.getFloorNum(), searchPo.getSmallStorageCode(), null, null,
                            OutboundContentPool.OUTBOUND_RECORD_NOT_SORT, searchPo.getMaterialNum(), LocalDateTime.now(), startStorage,
                            outboundOrder.getLogisticsCompanyCode());

                    String missionId = "Task_Outbound_whole_pallet_" + idWorker.nextId();

                    OutboundWholePalletMissionStatus initStatus;
                    if (searchPo.getInboundFreezePallet() == null) {
                        initStatus = OutboundWholePalletMissionStatus.WAIT_FOR_STORAGE;
                    } else {
                        initStatus = OutboundWholePalletMissionStatus.WAIT_FOR_ARRIVE;
                    }
                    WcsMission wcsMission = new WcsMission(null, missionId, outboundOrderDetail.getOutboundOrderCode(), MissionType.OUTBOUND_WHOLE_PALLET.name(),
                            initStatus.name(), LocalDateTime.now(), null, null,
                            1, outboundTaskRecord.getOutboundRecordId());
                    wcsMissionDao.insert(wcsMission);
                    outboundTaskRecordDao.insert(outboundTaskRecord);


                }


            }

        }
    }

    // 找板
    private void findStorageArea(OutboundOrderDetail outboundOrderDetail, OutboundOrder outboundOrder) {

        int needNum = outboundOrderDetail.getRequireNum() - outboundOrderDetail.getCompleteNum();


        // 2.找5F/6F
        List<MediumStorageAndFloorNum> canUseMediumStorageList = outboundDao.selectCanOutboundMediumSotrage(
                outboundOrderDetail.getMaterialCode(),
                needNum,
                StorageType.STORAGE_AREA_5F,
                StorageType.STORAGE_AREA_6F)
                .stream()
                .distinct()
                .collect(Collectors.toList());

        for (MediumStorageAndFloorNum mediumCodeAndFloor : canUseMediumStorageList) {
            Tuple<MediumStockUp> mediumStockUpTuple = outboundStorage5Or6FloorLocker.searchAndLockStockUpMedium(
                    mediumCodeAndFloor.getMediumStorageCode(),
                    mediumCodeAndFloor.getFloorNum(),
                    outboundOrderDetail.getMaterialCode(),
                    needNum,
                    false
            );
            if (!mediumStockUpTuple.success) continue;
            if (mediumStockUpTuple.value.getOutboundStockUpRecordList().isEmpty()) continue;

            needNum = mediumStockUpTuple.value.getRestNum();

            transformWcsMission(mediumStockUpTuple.value.getOutboundStockUpRecordList(), outboundOrderDetail, outboundOrder);

            if (outboundDao.selectCanOutboundMediumSotrage(
                    outboundOrderDetail.getMaterialCode(),
                    needNum,
                    StorageType.STORAGE_AREA_5F,
                    StorageType.STORAGE_AREA_6F).isEmpty()) break;
        }


        // 3.减StockUpNum
        int outboundNum = outboundOrderDetail.getRequireNum() - needNum;
        outboundOrderDetail.setCompleteNum(outboundNum);
        // 库区不够分拣死人
    }


    private void transformWcsMission(List<OutboundStockUpRecord> outboundStockUpRecordList, OutboundOrderDetail outboundOrderDetail, OutboundOrder outboundOrder) {
        // 2.转化，生成wcs任务
        List<OutboundTaskRecord> collect = outboundStockUpRecordList.stream()
                .map(searchPo -> new OutboundTaskRecord(null, "outbound_record_" + idWorker.nextId(), outboundOrderDetail.getOutboundOrderCode(),
                        outboundOrderDetail.getOutboundDetailId(), searchPo.getPalletCode(), searchPo.getMaterialCode(),
                        searchPo.getMaterialNum(), searchPo.getStartFloorNum(), searchPo.getStartSmallStorage(), null, null,
                        OutboundContentPool.OUTBOUND_RECORD_NOT_SORT, searchPo.getMaterialNum(), LocalDateTime.now(), searchPo.getStartSmallStorage(),
                        outboundOrder.getLogisticsCompanyCode())
                )
                .collect(Collectors.toList());

        for (OutboundTaskRecord outboundTaskRecord : collect) {
            String missionId = "Task_Outbound_whole_pallet_" + idWorker.nextId();

            WcsMission wcsMission = new WcsMission(null, missionId, outboundOrderDetail.getOutboundOrderCode(), MissionType.OUTBOUND_WHOLE_PALLET.name(),
                    OutboundWholePalletMissionStatus.WAIT_FOR_STORAGE.name(), LocalDateTime.now(), null, null,
                    1, outboundTaskRecord.getOutboundRecordId());
            wcsMissionDao.insert(wcsMission);
            outboundTaskRecordDao.insert(outboundTaskRecord);
        }

    }


    // 找尾数区整板 MANTISSA
    private void findMantissaArea(OutboundOrderDetail outboundOrderDetail, OutboundOrder outboundOrder) {

        int needNum = outboundOrderDetail.getRequireNum() - outboundOrderDetail.getCompleteNum();

        OutboundRestAreaSearchPo outboundRestAreaSearchPo = outboundDao.selectCanUseRestAreaPallet(
                outboundOrderDetail.getMaterialCode(),
                needNum,
                StorageType.MANTISSA_AREA_5F);

        if (outboundRestAreaSearchPo == null) return;

        smallStorageDao.updateOutboundFreezeBySmallStorageCode(
                outboundRestAreaSearchPo.getSmallStorageCode(),
                outboundRestAreaSearchPo.getPalletCode()
        );

        OutboundTaskRecord outboundTaskRecord = new OutboundTaskRecord(null, "outbound_record_" + idWorker.nextId(), outboundOrderDetail.getOutboundOrderCode(),
                outboundOrderDetail.getOutboundDetailId(), outboundRestAreaSearchPo.getPalletCode(), outboundRestAreaSearchPo.getMaterialCode(),
                outboundRestAreaSearchPo.getMaterialNum(), outboundRestAreaSearchPo.getFloorNum(), outboundRestAreaSearchPo.getSmallStorageCode(), null, null,
                OutboundContentPool.OUTBOUND_RECORD_NOT_SORT, outboundRestAreaSearchPo.getMaterialNum(), LocalDateTime.now(), outboundRestAreaSearchPo.getSmallStorageCode(),
                outboundOrder.getLogisticsCompanyCode());

        String missionId = "Task_Outbound_whole_pallet_" + idWorker.nextId();

        WcsMission wcsMission = new WcsMission(null, missionId, outboundOrderDetail.getOutboundOrderCode(), MissionType.OUTBOUND_WHOLE_PALLET.name(),
                OutboundWholePalletMissionStatus.WAIT_FOR_STORAGE.name(), LocalDateTime.now(), null, null,
                1, outboundTaskRecord.getOutboundRecordId());
        wcsMissionDao.insert(wcsMission);
        outboundTaskRecordDao.insert(outboundTaskRecord);


        // 3.减StockUpNum
        needNum = needNum - outboundRestAreaSearchPo.getMaterialNum();
        int outboundNum = outboundOrderDetail.getRequireNum() - needNum;
        outboundOrderDetail.setCompleteNum(outboundNum);
    }


    private void findRestPallet(OutboundOrderDetail outboundOrderDetail, OutboundOrder outboundOrder) {
        int needNum = outboundOrderDetail.getRequireNum() - outboundOrderDetail.getCompleteNum();
        if (needNum == 0) return;
        OutboundTaskRecord outboundTaskRecord = new OutboundTaskRecord(null, "outbound_record_" + idWorker.nextId(), outboundOrderDetail.getOutboundOrderCode(),
                outboundOrderDetail.getOutboundDetailId(), null, outboundOrderDetail.getMaterialCode(),
                needNum, null, null, null, null,
                OutboundContentPool.OUTBOUND_RECORD_SORT, null, LocalDateTime.now(), null, outboundOrder.getLogisticsCompanyCode());

        String missionId = "Task_Outbound_Sort_" + idWorker.nextId();

        WcsMission wcsMission = new WcsMission(null, missionId, outboundOrderDetail.getOutboundOrderCode(), MissionType.OUTBOUND_SORT.name(),
                OutboundSortMissionStatus.WAIT_FOR_PALLET_STORAGE.name(), LocalDateTime.now(), null, null,
                1, outboundTaskRecord.getOutboundRecordId());
        wcsMissionDao.insert(wcsMission);
        outboundTaskRecordDao.insert(outboundTaskRecord);

        outboundOrderDetail.setCompleteNum(outboundOrderDetail.getRequireNum());
    }

}
